/****************************************************************************************/
/*                                                                     					*/
/*  M16C/62P Group Program Collection                                  					*/
/*                                                                     					*/
/*  FILE NAME : command.c                                  		     					*/
/*  CPU       : This program is the Serial flash boot loader by Xmodem data transfer	*/
/*  HISTORY   : 2006.12.08 Ver 0.01                                    					*/
/*                                                                    	 				*/
/*  Copyright (C) 2006. Renesas Technology Corp.                       					*/
/*  Copyright (C) 2006. Renesas Solutions Corp.                        					*/
/*  All right reserved.                                                					*/
/*                                                                     					*/
/*****************************************************************************************/

/**************************************/
/*    include file                    */
/**************************************/
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "command.h"
#include "serial.h"
#include "delay_timer.h"
#include "xmodem.h"
#include "flash_header.h"

/****************************************/
/*    InitCommandHandler function       */
/****************************************/
void InitCommandHandler (void)
{
	InitSci();
	InitDelayTimer();
	
	// copy all RAM based code to RAM
	memcpy( (void *) INT_RAM_BASE_ADDRESS, (void *) RAM_BASED_ROM_ADDRESS, SIZE_OF_RAM_BASED_ROM );

	memcpy( (void *) 0x6000, (void *) 0xFE000, 0x100 ); //fify 20100119 copy VECTOR to RAM
	_asm("	ldc	#0,INTBH");
	_asm("	ldc	#06000h,INTBL");
}

#pragma SECTION program ram_based_prg

//
// functionality of this handler will be dependent on the communication hardware interface
//
/**************************************/
/*    RunCommandHandler function      */
/**************************************/
void RunCommandHandler (void)
{
	unsigned long far *ptr;
	pt2FunctionErase fp;		// function pointer used to call target program
	union union_c2s c2s;
	
	// check if bootloader should run by detecting activity for a 3 seconds on the
	// UART channel
	c2s.us = GetByte (3000);
	
	if ( c2s.uc[0] == TIMEOUT )
	{
		// timed out - call target app if reset vector is valid
		ptr = (unsigned long far *)( FIRST_USER_FLASH_ADDR + RESET_VECTOR_OFFSET );
		if ( *ptr != 0xffffffff )
		{
			// call user program and never return
			fp = (pt2FunctionErase) *ptr;
			fp( 0, 0);
		}
		else
		{
			// return back to main
			return;
		}
	}
	else
	{
		// serial activity detected
		// clear buffer
		// loop until we get a timeout
		PurgeComms( 250 );	
		// display menu
 		ShowMenu();
			
		while (1)
		{
			// timeout is not important as we know we have a byte waiting for us
			c2s.us = GetByte (100000);
			if ( c2s.uc[0] == OK )
			{
				switch ( c2s.uc[1] )
				{
					case '1':
					break;
					
					case '2':
						Command_2();		// erase all user flash blocks
					break;
					
					case '3':
						Command_3();		// erase specific flash block
					break;
					
					case '4':
						Command_4();		// xmodem download to flash memory
					break;

					case '5':
						Command_5();		// run target application
					break;
					
					default:
						ShowMenu();
				}
			}
		}
	}
}

//make sure the const string message is in the RAM area //fify20100119
#pragma SECTION rom string_in_ram

/**************************************/
/*    ShowMenu function               */
/**************************************/
void ShowMenu (void)
{
	SendString( "\r\nM16C/62P Bootloader -- " );
	SendString(__DATE__);
	SendString( "\r\n----------------------------------\r\n"  );
	SendString( "\r\n2......Erase User Area\r\n" );
	SendString( "\r\n3......Erase User Block\r\n" );
	SendString( "\r\n4......Program Flash via XModem Download\r\n" );
	SendString( "\r\n5......Start User Program\r\n" );
	SendByte( '>' );
}

/**************************************/
/*    Command_2 function              */
/**************************************/
void Command_2 (void)
{
	// erase all user flash blocks
	// the first flash block to be erased is 'FIRST_USER_FLASH_BLK' and the last
	// flash block to be erased is 'NO_OF_FLASH_BLOCKS' - 1
	// these values are defined in 'command.h' and 'flash_header.h'	
	
	unsigned char uc;
	unsigned char Status;
	union union_c2s c2s;
	
	
	SendString( "\r\nReally erase ALL user blocks (Y/N)?" );
	c2s.us = GetByte( 10000 );
	SendByte( c2s.uc[1] );
	if ( c2s.uc[0] == TIMEOUT )
	{
		SendString( "\r\nTimed out waiting for response" );
		return;
	}
	else if ( c2s.uc[0] == ERROR )
	{
		SendString( "\r\nComms error" );
		return;
	}
	else if ( !( ( c2s.uc[1] == 'y' ) || ( c2s.uc[1] == 'Y' ) ) )
	{
		SendString( "\r\nAll blocks erase cancelled" );
		return;
	}

	for( uc=FIRST_USER_FLASH_BLK; uc<NO_OF_FLASH_BLOCKS; uc++ )
	{
		SendString( "\r\nErasing block " );
		if( uc < 10 )
		{
			SendByte( uc + 48 );
		}
		else
		{
			SendByte( ( uc / 10 ) + 48 );
			SendByte( ( uc % 10 ) + 48 );
		}
		
		SendString ( "..." );
		// call the erase function with the block to be erased and a dummy value (0)
		Status = BlockErase ( uc, 0 );

		if ( Status == ERASE_FAIL )
		{
			SendString( "Erasing of block " );
			SendByte( uc + 48 );
			SendString( " FAILED\r\n" );
			return;
		}
	}

	SendString( "\r\nAll user blocks erased" );
}

/**************************************/
/*    Command_3 function              */
/**************************************/
void Command_3 (void)
{
	// erase the specified flash block
	union union_c2s c2s;
	unsigned char BlockNum, Count;
	
	BlockNum = 0;
	
	SendString( "\r\nEnter block no to be erased as 2-digit number e.g. 08\r\n" );
	SendString( ">D'" );
	
	for (Count=0; Count<2; Count++)
	{
		c2s.us = GetByte( 10000 );
		SendByte( c2s.uc[1] );
		if ( c2s.uc[0] == TIMEOUT )
		{
			SendString( "\r\nTimed out waiting for block number..." );
			return;
		}
		else if ( !isdigit( c2s.uc[1] ) )
		{
			SendString("\r\n");
			SendByte( CARRIAGE_RETURN );
			SendString( "Invalid character" );
			return;
		}
		else
		{
			if ( Count == 0 )
			{
				BlockNum = 10 * ( c2s.uc[1] - 48 );
			}
			else
			{
				BlockNum += ( c2s.uc[1] - 48 );
			}
		}
	}
	
	if ( !( ( BlockNum >= FIRST_USER_FLASH_BLK ) && ( BlockNum < NO_OF_FLASH_BLOCKS ) ) )
	{
		SendString( "\r\nInvalid block number" );
		return;
	}
	
	SendString( "\r\nErase block (Y/N)?" );
	c2s.us = GetByte( 10000 );
	SendByte( c2s.uc[1] );
	if ( c2s.uc[0] == TIMEOUT )
	{
		SendString( "\r\nTimed out waiting for confirmation" );
		return;
	}
	else if (  ( c2s.uc[1] == 'y' ) || ( c2s.uc[1] == 'Y' ) )
	{
		// call the erase function with the block to be erased and a dummy value (0)
		Count = BlockErase ( BlockNum, 0 );
		SendString( "\r\nErasing of block " );
		SendByte( BlockNum + 48 );
		if ( Count == ERASE_FAIL )
		{
			SendString( " FAILED" );
		} 
		else
		{
			SendString( " PASSED" );
		}
		return;
	}
	else
	{
		SendString( "\r\nBlock erase cancelled" );
		return;
	}
}

/**************************************/
/*    Command_4 function              */
/**************************************/
void Command_4 (void)
{
	unsigned char uc;
    // download data using xmodem and program into flash
    unsigned char Status, Shift;
	volatile unsigned long Address, ul;
	union union_c2s c2s;
	
	SendString( "\r\nReally erase ALL user blocks (Y/N)?" );
	c2s.us = GetByte( 10000 );
	SendByte( c2s.uc[1] );
	if ( c2s.uc[0] == TIMEOUT )
	{
		SendString( "\r\nTimed out waiting for response" );
		return;
	}
	else if ( c2s.uc[0] == ERROR )
	{
		SendString( "\r\nComms error" );
		return;
	}
	else if ( !( ( c2s.uc[1] == 'y' ) || ( c2s.uc[1] == 'Y' ) ) )
	{
		SendString( "\r\nAll blocks erase cancelled" );
		return;
	}

	for( uc=FIRST_USER_FLASH_BLK; uc<NO_OF_FLASH_BLOCKS; uc++ )
	{
		SendString( "\r\nErasing block " );
		if( uc < 10 )
		{
			SendByte( uc + 48 );
		}
		else
		{
			SendByte( ( uc / 10 ) + 48 );
			SendByte( ( uc % 10 ) + 48 );
		}
		
		SendString ( "..." );
		// call the erase function with the block to be erased and a dummy value (0)
		Status = BlockErase ( uc, 0 );

		if ( Status == ERASE_FAIL )
		{
			SendString( "Erasing of block " );
			SendByte( uc + 48 );
			SendString( " FAILED\r\n" );
			return;
		}
	}

	SendString( "\r\nAll user blocks erased" );
	
	// suggest download address as the first user flash address as usually this will be the case
	SendString( "\r\nDownload to address 0x" );
	SendString( FIRST_USER_FL_ADDR_TXT );
	SendString( " (Y/N)?" );
	c2s.us = GetByte( 10000 );
	SendByte( c2s.uc[1] );
	SendString("\r\n");
	if ( c2s.uc[0] == TIMEOUT )
	{
		SendString( "\r\nTimed out waiting for confirmation..." );
		return;
	}
	else if (  ( c2s.uc[1] == 'y' ) || ( c2s.uc[1] == 'Y' ) )
	{
		Address = FIRST_USER_FLASH_ADDR;
	}
	else 
	{
		SendString( "Enter Flash memory base address as a 32-bit hex value e.g. AABBCCDD" );
	
		Address = 0;
		Shift = 28;
		
		SendString( "\r\n>H'" );
		for (Status=0; Status<8; Status++)
		{		
			c2s.us = GetByte( 10000 );
			if ( c2s.uc[0] == TIMEOUT )
			{
				SendString( "\r\nTimed out waiting for address..." );
				return;
			}
			else if ( c2s.uc[0] == ERROR )
			{
				SendString( "\r\nComms error" );
				return;
			}
			else
			{
				if ( !isxdigit( c2s.uc[1] ) )
				{
					SendString( "\r\nInvalid character" );
					return;
				}
				SendByte( c2s.uc[1] );
				c2s.uc[1] = toupper( c2s.uc[1] );
				// convert ASCII number to hexadecimal value
				c2s.uc[1] =  isdigit( c2s.uc[1] ) ? c2s.uc[1] - 48 : c2s.uc[1] - 55;
				ul = (unsigned long) c2s.uc[1];
				ul = ul << Shift;
				Address |= ul;
				Shift -= 4;
			}		
		}
		
		SendString("\r\n");
	}
	
	if ( ( Address < FIRST_USER_FLASH_ADDR ) || ( Address > ( LAST_USER_FLASH_ADDR - FLASH_LINE_SIZE - 1 ) ) )
	{
		SendString( "Invalid start address - aborting" );
		return;
	}
	
	if ( ( Address % FLASH_LINE_SIZE ) != 0 )
	{
		SendString( "Start address not on a flash line boundary - aborting" );
		return;
	}
		
	SendString( "Program Flash (Y/N)?" );
	c2s.us = GetByte( 10000 );
	SendByte( c2s.uc[1] );
	if ( c2s.uc[0] == TIMEOUT )
	{
		SendString( "\r\nTimed out waiting for response" );
		return;
	}
	else if ( c2s.uc[0] == ERROR )
	{
		SendString( "\r\nComms error" );
		return;
	}
	else if ( !( ( c2s.uc[1] == 'y' ) || ( c2s.uc[1] == 'Y' ) ) )
	{
		SendString( "\r\nFlash programming cancelled" );
		return;
	}
	
	SendString( "\r\nStart XModem download...\r\n" );
	
	// start xmodem download and program the flash with the downloaded data
	Status = XmodemDownloadAndProgramFlash( Address );
		
	if ( Status == XM_TIMEOUT )
		SendString( "Timeout" );
	else if ( Status == XM_OK )
		SendString( "Download OK" );
	else if ( Status == XM_PROG_FAIL )
		SendString( "Flash program FAIL" );

	SendString("\r\n");
	
	PurgeComms( 1000 );
}

/**************************************/
/*    Command_5 function              */
/**************************************/
void Command_5 (void)
{
	// call the target application
	union union_c2s c2s;
	unsigned long far *ptr;
	pt2FunctionErase fp;
	
	SendString( "\r\nRun user program (Y/N)?" );
	c2s.us = GetByte( 10000 );
	SendByte( c2s.uc[1] );
	if ( c2s.uc[0] == TIMEOUT )
	{
		SendString( "\r\nTimed out waiting for response" );
		return;
	}
	else if ( c2s.uc[0] == ERROR )
	{
		SendString( "\r\nComms error" );
		return;
	}
	else if ( !( ( c2s.uc[1] == 'y' ) || ( c2s.uc[1] == 'Y' ) ) )
	{
		SendString( "\r\nRun user program request cancelled" );
		return;
	}
	
	SendString("\r\n");
	
	// run user program
	// check user reset address is not all erased
	ptr = (unsigned long far *) ( FIRST_USER_FLASH_ADDR + RESET_VECTOR_OFFSET );
	if ( *ptr != 0xffffffff )
	{
		SendString( "Running user program..." );
		// call user program and never return
		fp = (pt2FunctionErase) *ptr;
		fp( 0, 0);
	}
	else
	{
		// invalid user reset address
		SendString( "Invalid user reset address.  Staying put." );
		return;
	}
}
